<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf8">

  <meta name="description" content="WebGL State Diagram to help visualize WebGL" />
  <meta name="keywords" content="webgl graphics" />
  <meta name="thumbnail" content="https://webglfundamentals.org/webgl/lessons/resources/webglfundamentals-webgl-state-diagram.jpg" />

  <meta property="og:title" content="WebGLFundamentals WebGL State Diagram" />
  <meta property="og:type" content="website" />
  <meta property="og:image" content="https://webglfundamentals.org/webgl/lessons/resources/webglfundamentals-webgl-state-diagram.jpg" />
  <meta property="og:description" content="WebGL State Diagram to help visualize WebGL" />
  <meta property="og:url" content="https://webglfundamentals.org/webgl/lessons/resources/webgl-state-diagram.html">

  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@greggman">
  <meta name="twitter:creator" content="@greggman">
  <meta name="twitter:domain" content="webglfundamentals.org">
  <meta name="twitter:title" content="WebGLFundamentals WebGL State Diagram">
  <meta name="twitter:url" content="https://webglfundamentals.org/webgl/lessons/resources/webgl-state-diagram.html">
  <meta name="twitter:description" content="WebGL State Diagram to help visualize WebGL" />
  <meta name="twitter:image:src" content="https://webglfundamentals.org/webgl/lessons/resources/webglfundamentals-webgl-state-diagram.jpg">

  <title>WebGLFundamentals WebGL State Diagram</title>

  <link href="/webgl/lessons/resources/webglfundamentals-icon.png" rel="shortcut icon" type="image/png">


  <!-- <link href="/3rdparty/monokai-sublime.css" rel="stylesheet"> -->
  <link href="/3rdparty/prism-material-dark.css" rel="stylesheet">
  <!-- <link href="/3rdparty/tomorrow-night-bright.css" rel="stylesheet"> -->
  <!-- <link href="/3rdparty/atom-one-dark-reasonable.css" rel="stylesheet">  --> 
  <link href="webgl-state-diagram/state-diagram.css" rel="stylesheet">
</head>
<body>
<div id="everything">
  <div id="diagram">
    <div id="canvas">
      <div class="name">canvas</div>
      <canvas></canvas>
    </div>
    <div id="global-state" class="window-content">
      <div class="name">global state</div>
    </div>

    <svg id="arrows" width="100%" height="100%">
      <defs>
      </defs>
    </svg>
  </div>
  <div id="codearea">
    <div id="ui">
      <div id="step">⇥</div>
      <div id="run">⇉</div>
      <div id="restart">↺</div>
      <div id="help">?</div>
      <div>
        <select id="example"></select>
      </div>
    </div>
    <div id="code"></div>
  </div>
</div>
<div class="get-webgl">
<pre>
 /\_/\
((@v@))  WebGL Owl
():::()  sees no WebGL
 VV-VV
</pre>
</div>
<div id="hint"></div>

<template id="buffer-template">
<div class="webgl-object window-content buffer" data-help="
Buffers contain data provided to attributes.

Buffers are created with

```js
const buffer = gl.createBuffer();
```
">
  <div class="name">buffer</div>
  <div class="type"></div>
  <div class="content"></div>
</div>
</template>

<template id="renderbuffer-template">
<div class="webgl-object window-content renderbuffer" data-help="
Renderbuffers are an opaque kind of texture and can not usually
be read directly. Common examples are depth renderbuffers, stencil renderbuffers,
and in WebGL2 multi-sample renderbuffers for MSAA anti-aliasing.

Renderbuffers are created with

```js
const renderbuffer = gl.createRenderbuffer();
```
">
  <div class="name">renderbuffer</div>
  <div class="format"></div>
</div>
</template>

<template id="framebuffer-template">
<div class="webgl-object window-content framebuffer" data-help="
Framebuffers are an array of texture and renderbuffer attachments
used to bind them as a set as a render target, the place pixels
and/or depth values and/or stencil values will be written to and/or
read from.

Framebuffers are created with

```js
const framebuffer = gl.createFramebuffer();
```
">
  <div class="name">framebuffer</div>
  <div class="attachments"></div>
  <div class="state"></div>
</div>
</template>

<template id="texture-template">
<div class="webgl-object window-content texture" data-help="
Textures provide random access data to shaders. Most often
they contain image data but not always.

Textures are created with

```js
const tex = gl.createTexture();
```

and bound to a texture unit bind point (target) with

```
gl.activeTexture(gl.TEXTURE0 + texUnitIndex);
const bindPoint = gl.TEXTURE_2D;
gl.bindTexture(bindPoint, tex);
```

All texture functions reference textures
through the bind points on the active texture unit.
ie.

```js
texture = textureUnits[activeTexture][bindPoint]
```

For more details see [the article on textures](/webgl/lessons${langPathSegment}/webgl-3d-textures.html).
">
  <div class="name">texture</div>
  <div class="mips"></div>
</div>
</template>

<template id="vertex-array-template">
  <div class="webgl-object window-content vertex-array" data-help="
Vertex Arrays contain all attribute state. Attributes define how
to pull data out of buffers to supply to a vertex shader.

Normally there is only the 1 default vertex array in WebGL 1.0.

You can create more vertex arrays with the
[`OES_vertex_array_object`](https://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/)
extension.

```js
const ext = gl.getExtension('OES_vertex_array_object')
const vertexArray = ext.createVertexArrayOES();
```

and bind one (make it the current vertex array) with

```js
ext.bindVertexArrayOES(someVertexArray);
```

Passing `null` to `ext.bindVertexArrayOES` binds the default vertex array.
">
    <div class="name">vertex array</div>
    <div class="state-table">
    </div>
  </div>
</template>

<template id="vertex-shader-template">
  <div class="webgl-object window-content shader vertex-shader" data-help="
A vertex shader's sole responsibility is to set `gl_Position`
to a clip space position. To create one use:

```js
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, glslString);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) === 0) {
  console.error(gl.getShaderInfoLog(shader));
}
```
">
    <div class="name">shader</div>
  </div>
</template>

<template id="fragment-shader-template">
  <div class="webgl-object window-content shader fragment-shader" data-help="
A fragment shader's sole responsibility is to set `gl_FragColor`
to a color. To create one use:

```js
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, glslString);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) === 0) {
  console.error(gl.getShaderInfoLog(shader));
}
```
">
    <div class="name">shader</div>
  </div>
</template>

<template id="program-template">
  <div class="webgl-object window-content program" data-help="
A program is a combination of a vertex shader
and a fragment shader *linked* together.

```js
const program = gl.createProgram();
gl.attachShader(program, someVertexShader);
gl.attachShader(program, someFragmentShader);
gl.linkProgram(program);
if (gl.getProgramParameter(program, gl.LINK_STATUS) === 0) {
  console.error(gl.getProgramInfoLog(program));
}
```
">
    <div class="name">program</div>
  </div>
</template>

</body>
<script id="docs-start" type="not-js">
# WebGL State diagram

This is an attempt to give a visual representation
of most of WebGL's internal state.

WebGL is really just an API to run shaders. The only
functions that actually write pixels are `gl.clear`,
`gl.drawArrays` and `gl.drawElements`. That's it!
All other API calls just setup internal state for when
those 3 functions are called.

## Instructions

* Click <span class="ui-button">⇥</span> to step one line
at a time and watch what WebGL state is affected.

* You might need to zoom out in your browser to see everything

* Anything you hover over that shows a 
  <span class="help-outline">red outline</span>
  can be clicked to show help.

* Choose different samples on the top right.

## Notes:

* A programs only indirectly
  reference textures via texture units by index. Similarly 
  programs only indirectly reference buffers via vertex
  array attribute index. The arrows showing
  these connection are dotted to indicate they are not
  direct connections.

  Program attributes are not connected to a specific vertex array
  nor are program uniforms connected to a particular texture.

* **Not all state is shown.**
</script>
<script id="draw-cubes" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

// make a canvas with text in the center
const makeTextCanvas = (text, width, height, color) => {
  const ctx = document.createElement('canvas').getContext('2d')
  ctx.canvas.width = width
  ctx.canvas.height = height
  ctx.font = `bold ${height * 5 / 6 | 0}px sans-serif`
  ctx.textAlign = 'center'
  ctx.textBaseline = 'middle'
  ctx.fillStyle = color
  ctx.fillText(text, width / 2, height / 2)
  return ctx.canvas
};

const vsGLSL = `
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texcoord;

uniform mat4 projection;
uniform mat4 modelView;

varying vec3 v_normal;
varying vec2 v_texcoord;

void main() {
    gl_Position = projection * modelView * position;
    v_normal = mat3(modelView) * normal;
    v_texcoord = texcoord;
}
`;

const fsGLSL = `
precision highp float;

varying vec3 v_normal;
varying vec2 v_texcoord;

uniform sampler2D diffuse;
uniform sampler2D decal;
uniform vec4 diffuseMult;
uniform vec3 lightDir;

void main() {
    vec3 normal = normalize(v_normal);
    float light = dot(normal, lightDir) * 0.5 + 0.5;
    vec4 color = texture2D(diffuse, v_texcoord) * diffuseMult;
    vec4 decalColor = texture2D(decal, v_texcoord);
    decalColor.rgb *= decalColor.a;
    color = color * (1.0 - decalColor.a) + decalColor; 
    gl_FragColor = vec4(color.rgb * light, color.a);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};

// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');
const normalLoc = gl.getAttribLocation(prg, 'normal');
const texcoordLoc = gl.getAttribLocation(prg, 'texcoord');

const projectionLoc = gl.getUniformLocation(prg, 'projection');
const modelViewLoc = gl.getUniformLocation(prg, 'modelView');
const diffuseLoc = gl.getUniformLocation(prg, 'diffuse');
const decalLoc = gl.getUniformLocation(prg, 'decal');
const diffuseMultLoc = gl.getUniformLocation(prg, 'diffuseMult');
const lightDirLoc = gl.getUniformLocation(prg, 'lightDir');

// vertex positions for a cube
const cubeVertexPositions = new Float32Array([
    1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1,
]);
// vertex normals for a cube
const cubeVertexNormals = new Float32Array([
    1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
]);
// vertex texture coordinates for a cube
const cubeVertexTexcoords = new Float32Array([
    1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
]);
// vertex indices for the triangles of a cube
// the data above defines 24 vertices. We need to draw 12
// triangles, 2 for each size, each triangle needs
// 3 vertices so 12 * 3 = 36
const cubeVertexIndices = new Uint16Array([
    0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
],);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexPositions, gl.STATIC_DRAW);

const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexNormals, gl.STATIC_DRAW);

const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexTexcoords, gl.STATIC_DRAW);

const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndices, gl.STATIC_DRAW);

const checkerTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.LUMINANCE,     // internal format
    4,                // width
    4,                // height
    0,                // border
    gl.LUMINANCE,     // format
    gl.UNSIGNED_BYTE, // type
    new Uint8Array([  // data
      192, 128, 192, 128,
      128, 192, 128, 192,
      192, 128, 192, 128,
      128, 192, 128, 192,
    ]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

const decalTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, decalTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.RGBA,          // internal format
    gl.RGBA,          // format
    gl.UNSIGNED_BYTE, // type
    makeTextCanvas('F', 32, 32, 'red'));
gl.generateMipmap(gl.TEXTURE_2D);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

gl.clearColor(0.5, 0.7, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc,  // location
    3,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.enableVertexAttribArray(normalLoc);
gl.vertexAttribPointer(
    normalLoc,  // location
    3,          // size (components per iteration)
    gl.FLOAT,   // type of to get from buffer
    false,      // normalize
    0,          // stride (bytes to advance each iteration)
    0,          // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.enableVertexAttribArray(texcoordLoc);
gl.vertexAttribPointer(
    texcoordLoc,  // location
    2,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

gl.useProgram(prg);

// Picking unit 6 just to be different. The default of 0
// would render but would show less state changing.
let texUnit = 6;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.uniform1i(diffuseLoc, texUnit);

texUnit = 3;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, decalTexture);
gl.uniform1i(decalLoc, texUnit);

gl.uniform3fv(lightDirLoc, m4.normalize([1, 5, 8]));

const projection = m4.perspective(
  60 * Math.PI / 180,  // fov
  gl.canvas.clientWidth / gl.canvas.clientHeight,  // aspect
  0.1,  // near
  10,   // far
);
gl.uniformMatrix4fv(projectionLoc, false, projection);

// draw center cube

let modelView = m4.identity();
modelView = m4.translate(modelView, 0, 0, -4);
modelView = m4.xRotate(modelView, 0.5);
modelView = m4.yRotate(modelView, 0.5);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.uniform4fv(diffuseMultLoc, [0.7, 1, 0.7, 1]);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// draw left cube

modelView = m4.identity();
modelView = m4.translate(modelView, -3, 0, -4);
modelView = m4.xRotate(modelView, 0.5);
modelView = m4.yRotate(modelView, 0.8);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.uniform4fv(diffuseMultLoc, [1, 0.7, 0.7, 1]);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// draw right cube

modelView = m4.identity();
modelView = m4.translate(modelView, 3, 0, -4);
modelView = m4.xRotate(modelView, 0.6);
modelView = m4.yRotate(modelView, -0.6);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.uniform4fv(diffuseMultLoc, [0.7, 0.7, 1, 1]);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);
</script>
<script id="smallest" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
<script id="smallest-glsl" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const vsGLSL = `
void main() {
    gl_Position = vec4(0, 0, 0, 1);
    gl_PointSize = 100.0;
}
`;

const fsGLSL = `
precision highp float;

void main() {
    gl_FragColor = vec4(1, 0.5, 0, 1);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};

gl.useProgram(prg);

// draw 1 point
gl.drawArrays(gl.POINTS, 0, 1);
</script>
<script id="triangle" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const vsGLSL = `
attribute vec4 position;
void main() {
    gl_Position = position;
}
`;

const fsGLSL = `
precision highp float;

void main() {
    gl_FragColor = vec4(0, 1, 0.5, 1);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};


// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');

// in clip space
const vertexPositions = new Float32Array([
    0,   0.7,
  0.5,  -0.7,
 -0.5,  -0.7,
]);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexPositions, gl.STATIC_DRAW);

gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc,  
    2,            // 2 values per vertex shader iteration
    gl.FLOAT,     // data is 32bit floats
    false,        // don't normalize
    0,            // stride (0 = auto)
    0,            // offset into buffer
);

gl.useProgram(prg);

// compute 3 vertices for 1 triangle
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
<script id="rainbow-triangle" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const vsGLSL = `
attribute vec4 position;
attribute vec4 color;

varying vec4 v_color;

void main() {
    gl_Position = position;
    v_color = color;
}
`;

const fsGLSL = `
precision highp float;

varying vec4 v_color;

void main() {
    gl_FragColor = v_color;
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};


// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');
const colorLoc = gl.getAttribLocation(prg, 'color');

// in clip space
const vertexPositions = new Float32Array([
    0,   0.7,
  0.5,  -0.7,
 -0.5,  -0.7,
]);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexPositions, gl.STATIC_DRAW);

const vertexColors = new Uint8Array([
    255, 0, 0, 255,
    0, 255, 0, 255,
    0, 0, 255, 255,
]);

const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexColors, gl.STATIC_DRAW);

gl.enableVertexAttribArray(positionLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(
    positionLoc,  
    2,            // 2 values per vertex shader iteration
    gl.FLOAT,     // data is 32bit floats
    false,        // don't normalize
    0,            // stride (0 = auto)
    0,            // offset into buffer
);

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(
    colorLoc,  
    4,                // 4 values per vertex shader iteration
    gl.UNSIGNED_BYTE, // data is 8bit unsigned bytes
    true,             // do normalize
    0,                // stride (0 = auto)
    0,                // offset into buffer
);

gl.useProgram(prg);

// compute 3 vertices for 1 triangle
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
<script id="draw-cube-on-cube" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const vsGLSL = `
attribute vec4 position;
attribute vec3 normal;
attribute vec2 texcoord;

uniform mat4 projection;
uniform mat4 modelView;

varying vec3 v_normal;
varying vec2 v_texcoord;

void main() {
    gl_Position = projection * modelView * position;
    v_normal = mat3(modelView) * normal;
    v_texcoord = texcoord;
}
`;

const fsGLSL = `
precision highp float;

varying vec3 v_normal;
varying vec2 v_texcoord;

uniform sampler2D diffuse;
uniform vec3 lightDir;

void main() {
    vec3 normal = normalize(v_normal);
    float light = dot(normal, lightDir) * 0.5 + 0.5;
    vec4 color = texture2D(diffuse, v_texcoord);
    gl_FragColor = vec4(color.rgb * light, color.a);
}
`;

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsGLSL);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(vertexShader))
};

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsGLSL);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
  throw new Error(gl.getShaderInfoLog(fragmentShader))
};

const prg = gl.createProgram();
gl.attachShader(prg, vertexShader);
gl.attachShader(prg, fragmentShader);
gl.linkProgram(prg);
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
  throw new Error(gl.getProgramInfoLog(prg))
};

// NOTE! These are only here to unclutter the diagram.
// It is safe to detach and delete shaders once
// a program is linked though it is arguably not common.
// and I usually don't do it.
gl.detachShader(prg, vertexShader);
gl.deleteShader(vertexShader);
gl.detachShader(prg, fragmentShader);
gl.deleteShader(fragmentShader);

const positionLoc = gl.getAttribLocation(prg, 'position');
const normalLoc = gl.getAttribLocation(prg, 'normal');
const texcoordLoc = gl.getAttribLocation(prg, 'texcoord');

const projectionLoc = gl.getUniformLocation(prg, 'projection');
const modelViewLoc = gl.getUniformLocation(prg, 'modelView');
const diffuseLoc = gl.getUniformLocation(prg, 'diffuse');
const lightDirLoc = gl.getUniformLocation(prg, 'lightDir');

// vertex positions for a cube
const cubeVertexPositions = new Float32Array([
    1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1,
]);
// vertex normals for a cube
const cubeVertexNormals = new Float32Array([
    1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
]);
// vertex texture coordinates for a cube
const cubeVertexTexcoords = new Float32Array([
    1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
]);
// vertex indices for the triangles of a cube
// the data above defines 24 vertices. We need to draw 12
// triangles, 2 for each size, each triangle needs
// 3 vertices so 12 * 3 = 36
const cubeVertexIndices = new Uint16Array([
    0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
],);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexPositions, gl.STATIC_DRAW);

const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexNormals, gl.STATIC_DRAW);

const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, cubeVertexTexcoords, gl.STATIC_DRAW);

const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndices, gl.STATIC_DRAW);

const checkerTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.LUMINANCE,     // internal format
    4,                // width
    4,                // height
    0,                // border
    gl.LUMINANCE,     // format
    gl.UNSIGNED_BYTE, // type
    new Uint8Array([  // data
      192, 128, 192, 128,
      128, 192, 128, 192,
      192, 128, 192, 128,
      128, 192, 128, 192,
    ]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

const fbTextureWidth = 128;
const fbTextureHeight = 128;
const fbTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, fbTexture);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                // mip level
    gl.RGBA,          // internal format
    fbTextureWidth,   // width
    fbTextureHeight,  // height
    0,                // border
    gl.RGBA,          // format
    gl.UNSIGNED_BYTE, // type
    null,             // data
)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

const depthRB = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthRB);
gl.renderbufferStorage(
    gl.RENDERBUFFER,
    gl.DEPTH_COMPONENT16,  // format
    fbTextureWidth,        // width,
    fbTextureHeight,       // height,
);

const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fbTexture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRB);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

// --------------------------------------
// First draw a cube to the texture attached to the framebuffer

gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.viewport(0, 0, fbTextureWidth, fbTextureHeight);

gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc,  // location
    3,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.enableVertexAttribArray(normalLoc);
gl.vertexAttribPointer(
    normalLoc,  // location
    3,          // size (components per iteration)
    gl.FLOAT,   // type of to get from buffer
    false,      // normalize
    0,          // stride (bytes to advance each iteration)
    0,          // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.enableVertexAttribArray(texcoordLoc);
gl.vertexAttribPointer(
    texcoordLoc,  // location
    2,            // size (components per iteration)
    gl.FLOAT,     // type of to get from buffer
    false,        // normalize
    0,            // stride (bytes to advance each iteration)
    0,            // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

gl.useProgram(prg);

// Picking unit 6 just to be different. The default of 0
// would render but would show less state changing.
let texUnit = 6;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
gl.uniform1i(diffuseLoc, texUnit);

gl.uniform3fv(lightDirLoc, m4.normalize([1, 5, 8]));

// We need a perspective matrix that matches the
// aspect of the framebuffer texture
const projection = m4.perspective(
  60 * Math.PI / 180,                // fov
  fbTextureWidth / fbTextureHeight,  // aspect
  0.1,                               // near
  10,                                // far
);
gl.uniformMatrix4fv(projectionLoc, false, projection);

let modelView = m4.identity();
modelView = m4.translate(modelView, 0, 0, -4);
modelView = m4.xRotate(modelView, 0.5);
modelView = m4.yRotate(modelView, 0.5);

gl.uniformMatrix4fv(modelViewLoc, false, modelView);

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);

// --------------------------------------
// Now draw a cube to the canvas using
// the texture attached to the framebuffer

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

gl.clearColor(0, 0, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

// We need a perspective matrix that matches the
// aspect of the canvas
const projection = m4.perspective(
  60 * Math.PI / 180,  // fov
  gl.canvas.clientWidth / gl.canvas.clientHeight,  // aspect
  0.1,  // near
  10,   // far
);
gl.uniformMatrix4fv(projectionLoc, false, projection);

// Picking unit 3 just to be different. We could have
// stated with the same as above.
let texUnit = 3;
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, fbTexture);
gl.uniform1i(diffuseLoc, texUnit);

// We didn't have to change attributes or other
// uniforms as we're just drawing the same cube vertices
// at the same place in front of the camera.

gl.drawElements(
    gl.TRIANGLES,
    36,                // num vertices to process
    gl.UNSIGNED_SHORT, // type of indices
    0,                 // offset on bytes to indices
);
</script>
<script id="use-2-programs" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const vsGLSL1 = `
  attribute vec4 position;

  void main() {
    gl_Position = position;
    gl_PointSize = 10.0;
  }
`;

const fsGLSL1 = `
  precision highp float;
  uniform vec4 color;
  void main() {
    gl_FragColor = color;
  }
`;

const vsGLSL2 = `
  attribute vec4 position;
  attribute vec4 color;
  varying vec4 v_color;
  void main() {
    gl_Position = position;
    gl_PointSize = 10.0;
    v_color = color;
  }
`;

const fsGLSL2 = `
  precision highp float;
  varying vec4 v_color;
  void main() {
    gl_FragColor = v_color;
  }
`;

const createShader = function(gl, type, glsl) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, glsl)
  gl.compileShader(shader)
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader))
  }
  return shader
};

const compileShadersAndLinkProgram = function(gl, prg, vsGLSL, fsGLSL) {
  const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsGLSL)
  const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsGLSL)
  gl.attachShader(prg, vertexShader)
  gl.attachShader(prg, fragmentShader)
  gl.linkProgram(prg)
  if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
    throw new Error(gl.getProgramInfoLog(prg))
  }
  // NOTE! These are only here to unclutter the diagram.
  // It is safe to detach and delete shaders once
  // a program is linked though it is arguably not common.
  // and I usually don't do it.
  gl.detachShader(prg, vertexShader)
  gl.deleteShader(vertexShader)
  gl.detachShader(prg, fragmentShader)
  gl.deleteShader(fragmentShader)
  return prg
};

// program 1 uses 1 attribute and 1 uniform

const prog1 = gl.createProgram();
compileShadersAndLinkProgram(gl, prog1, vsGLSL1, fsGLSL1);
const prog1Locs = {
  position: gl.getAttribLocation(prog1, 'position'),
  color: gl.getUniformLocation(prog1, 'color'),
};

// program 2 uses 2 attributes and no uniforms
const prog2 = gl.createProgram();
compileShadersAndLinkProgram(gl, prog2, vsGLSL2, fsGLSL2);
const prog2Locs = {
  position: gl.getAttribLocation(prog2, 'position'),
  color: gl.getAttribLocation(prog2, 'color'),
};

// vertex positions for a  ⃟
const prog1VertexPositions = new Float32Array([
   -0.2,  0,
   -0.8,  0,
   -0.3, -0.2,
   -0.7, -0.2,
   -0.3,  0.2,
   -0.7,  0.2,
   -0.4,  0.4,
   -0.6,  0.4,
   -0.4, -0.4,
   -0.6, -0.4,
   -0.5,  0.6,
   -0.5, -0.6,
]);

// vertex positions for a +
const prog2VertexPositions = new Float32Array([
   0.5,  0,
   0.5, -0.2,
   0.5,  0.2,
   0.5, -0.4,
   0.5,  0.4,
   0.4,  0,
   0.6,  0,
   0.3,  0,
   0.7,  0,
]);

const prog2VertexColors = new Float32Array([
   1, 0, 0,  // red
   0, 0, 1,  // blue
   0, 0, 1,
   0, 1, 0,  // green
   0, 1, 0,
   0, 1, 1,  // cyan
   0, 1, 1,
   1, 0, 1,  // magenta
   1, 0, 1,
]);

const position1Buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position1Buffer);
gl.bufferData(gl.ARRAY_BUFFER, prog1VertexPositions, gl.STATIC_DRAW);

const position2Buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position2Buffer);
gl.bufferData(gl.ARRAY_BUFFER, prog2VertexPositions, gl.STATIC_DRAW);

const color2Buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color2Buffer);
gl.bufferData(gl.ARRAY_BUFFER, prog2VertexColors, gl.STATIC_DRAW);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

// --------------------------------------
// First draw a diamond of points with the first program

// set the attributes to what's needed for program 1
gl.bindBuffer(gl.ARRAY_BUFFER, position1Buffer);
gl.enableVertexAttribArray(prog1Locs.position);
gl.vertexAttribPointer(
    prog1Locs.position, // location
    2,                  // size (components per iteration)
    gl.FLOAT,           // type of to get from buffer
    false,              // normalize
    0,                  // stride (bytes to advance each iteration)
    0,                  // offset (bytes from start of buffer)
);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

gl.useProgram(prog1);

// orange
gl.uniform4fv(prog1Locs.color, [1, 0.7, 0.5, 1]);

// draw 12 points
gl.drawArrays(gl.POINTS, 0, 12);


// --------------------------------------
// Second, draw a plus of points with the second program

// set the attributes to what's needed for program 1
gl.bindBuffer(gl.ARRAY_BUFFER, position2Buffer);
gl.enableVertexAttribArray(prog2Locs.position);
gl.vertexAttribPointer(
    prog2Locs.position, // location
    2,                  // size (components per iteration)
    gl.FLOAT,           // type of to get from buffer
    false,              // normalize
    0,                  // stride (bytes to advance each iteration)
    0,                  // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ARRAY_BUFFER, color2Buffer);
gl.enableVertexAttribArray(prog2Locs.color);
gl.vertexAttribPointer(
    prog2Locs.color,    // location
    3,                  // size (components per iteration)
    gl.FLOAT,           // type of to get from buffer
    false,              // normalize
    0,                  // stride (bytes to advance each iteration)
    0,                  // offset (bytes from start of buffer)
);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

gl.useProgram(prog2);

// draw 9 points
gl.drawArrays(gl.POINTS, 0, 9);
</script>
<script id="disabled-vertex-attrib" type="not-js">
'use strict';

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const vsGLSL = `
  attribute vec4 position;
  attribute vec4 color;

  uniform vec4 offset;

  varying vec4 v_color;

  void main() {
    gl_Position = position + offset;
    v_color = color;
  }
`;

const fsGLSL = `
  precision highp float;
  varying vec4 v_color;
  void main() {
    gl_FragColor = v_color;
  }
`;

const createShader = function(gl, type, glsl) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, glsl)
  gl.compileShader(shader)
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(shader))
  }
  return shader
};

const compileShadersAndLinkProgram = function(gl, prg, vsGLSL, fsGLSL) {
  const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsGLSL)
  const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsGLSL)
  gl.attachShader(prg, vertexShader)
  gl.attachShader(prg, fragmentShader)
  gl.linkProgram(prg)
  if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
    throw new Error(gl.getProgramInfoLog(prg))
  }
  // NOTE! These are only here to unclutter the diagram.
  // It is safe to detach and delete shaders once
  // a program is linked though it is arguably not common.
  // and I usually don't do it.
  gl.detachShader(prg, vertexShader)
  gl.deleteShader(vertexShader)
  gl.detachShader(prg, fragmentShader)
  gl.deleteShader(fragmentShader)
  return prg
};

const prog = gl.createProgram();
compileShadersAndLinkProgram(gl, prog, vsGLSL, fsGLSL);
const positionLoc = gl.getAttribLocation(prog, 'position');
const colorLoc = gl.getAttribLocation(prog, 'color');
const offsetLoc = gl.getUniformLocation(prog, 'offset');

// vertex positions for triangle
const vertexPositions = new Float32Array([
   0.0,  0.4,
  -0.4, -0.4,
   0.4, -0.4,
]);

const vertexColors = new Float32Array([
  1, 1, 0, 1,  // yellow
  0, 1, 1, 1,  // cyan
  1, 0, 1, 1,  // magenta
]);

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexPositions, gl.STATIC_DRAW);

const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexColors, gl.STATIC_DRAW);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

// above this line is initialization code
// --------------------------------------
// below is rendering code.

// --------------------------------------
// First draw a triangle with a different color for each vertex

// set the attributes
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
    positionLoc, // location
    2,           // size (components per iteration)
    gl.FLOAT,    // type of to get from buffer
    false,       // normalize
    0,           // stride (bytes to advance each iteration)
    0,           // offset (bytes from start of buffer)
);

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.enableVertexAttribArray(colorLoc);
gl.vertexAttribPointer(
    colorLoc, // location
    4,        // size (components per iteration)
    gl.FLOAT, // type of to get from buffer
    false,    // normalize
    0,        // stride (bytes to advance each iteration)
    0,        // offset (bytes from start of buffer)
);

// this is not needed. It's just here to unclutter the diagram
gl.bindBuffer(gl.ARRAY_BUFFER, null);

gl.useProgram(prog);

// draw on left
gl.uniform4fv(offsetLoc, [-0.5, 0, 0, 0]);

// draw 3 vertices (1 triangle)
gl.drawArrays(gl.TRIANGLES, 0, 3);

// Now draw the triangle again in a solid color 
// by turning off the color attribute and setting
// an attribute values
gl.disableVertexAttribArray(colorLoc);
gl.vertexAttrib4fv(colorLoc, [0.3, 0.6, 0.9, 1]);

// draw on right
gl.uniform4fv(offsetLoc, [0.5, 0, 0, 0]);

// draw 3 vertices (1 triangle)
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
<script src="/webgl/resources/chroma.min.js"></script>
<script src="/3rdparty/showdown.min.js"></script>
<script src="/3rdparty/prism.js" data-manual></script>
<script src="../../resources/m4.js"></script>
<script src="webgl-state-diagram/eval-helper.js"></script>
<script type="module">
import webglStateDiagram from './webgl-state-diagram/state-diagram.js';
webglStateDiagram({
  webglVersion: 'webgl',
  examples: {
    'draw-cubes': {
      name: 'Draw Cubes',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+10',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'n-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 't-buffer',     base: 'normalBuffer',   x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'i-buffer',     base: 'texcoordBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'ch-texture',   base: 'indexBuffer',    x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'dc-texture',   base: 'checkerTexture', x: 'left:left-20',   y: 'top:top+20', },
      ],
    },
    'draw-cube-on-cube': {
      name: 'Draw Cube on Cube',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+10',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'n-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 't-buffer',     base: 'normalBuffer',   x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'i-buffer',     base: 'texcoordBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'ch-texture',   base: 'indexBuffer',    x: 'left:left-0',    y: 'top:bottom+10', },
        { note: 'fb-texture',   base: 'checkerTexture', x: 'left:left-20',   y: 'top:top+20', },
        { note: 'renderbuffer', base: 'texcoordBuffer', x: 'right:left+100', y: 'top:top+40', },
        { note: 'framebuffer',  base: 'program',        x: 'left:left+0',    y: 'top:bottom+30', },
      ],
    },
    'smallest': {
      name: 'Smallest',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+10',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
      ],
    },
    'smallest-glsl': {
      name: 'Smallest GLSL',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+10',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
      ],
    },
    'triangle': {
      name: 'Triangle',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+10',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
      ],
    },
    'rainbow-triangle': {
      name: 'Rainbow Triangle',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+10',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'c-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
      ],
    },
    'use-2-programs': {
      name: 'Use 2 Programs',
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+10',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'program',      base: 'prog1',          x: 'left:left+10',   y: 'top:bottom+10', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'p1-buffer',    base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'p2-buffer',    base: 'position1Buffer',x: 'left:left+0',    y: 'top:bottom+10', },
        { note: 'c-buffer',     base: 'position2Buffer',x: 'left:left-0',    y: 'top:bottom+10', },
      ],
    },
    'disabled-vertex-attrib': {
      name: 'Disabled Vertex Attrib',
      adjust: [
        {cmd: 'expand',   id: 'globalUI.attribValueState'},
      ],
      windowPositions: [
        { note: 'canvas',       base: '#diagram',       x: 'right:right-10', y: 'top:top+10', },
        { note: 'vertex-array', base: '#diagram',       x: 'left:left+460',   y: 'bottom:bottom-10', },
        { note: 'global-state', base: '#diagram',       x: 'left:left+10',   y: 'top:top+10', },
        { note: 'program',      base: 'global state',   x: 'left:right+60',  y: 'top:top+0', },
        { note: 'v-shader',     base: 'canvas',         x: 'left:left-50',   y: 'top:bottom+10', },
        { note: 'f-shader',     base: 'vertexShader',   x: 'left:left-10',   y: 'top:bottom-90', },
        { note: 'p-buffer',     base: 'canvas',         x: 'left:left+70',   y: 'top:bottom+10', },
        { note: 'c-buffer',     base: 'positionBuffer', x: 'left:left-0',    y: 'top:bottom+10', },
      ],
    },
  },
});
</script>
</html>
